﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

using SharedGenomics.Core;

namespace SharedGenomics.Workbench.Core
{
    public abstract class MultiStageTask<T> : TaskBase where T : class, new()
    {
        private int _currentStageIndex;
        private IPresenter _currentPresenter;
        private IList<Type> _stagePresenterTypes;

        public MultiStageTask(string name, string displayName, PresenterFactory presenterFactory)
            : base(name, displayName, presenterFactory)
        {
            //create new context
            this.TaskContext = new T();
        }

        public void MoveNext()
        {
            ExceptionHelper.ThrowIf<InvalidOperationException>(this._currentStageIndex >= this._stagePresenterTypes.Count - 1, "No next stage to move to");
            this.MoveToStage(this._currentStageIndex + 1);
            this.OnCurrentPresenterChanged(EventArgs.Empty);
        }

        public void MovePrevious()
        {
            ExceptionHelper.ThrowIf<InvalidOperationException>(this._currentStageIndex == 0, "No previous stage to move to");
            this.MoveToStage(this._currentStageIndex - 1);
            this.OnCurrentPresenterChanged(EventArgs.Empty);
        }

        private void MoveToStage(int stageIndex)
        {
            Debug.Assert(stageIndex.IsBetween(0, this._stagePresenterTypes.Count - 1, true));

            ITaskStagePresenter<T> stagePresenter = (ITaskStagePresenter<T>)this.CreatePresenter(this._stagePresenterTypes[stageIndex]);
            stagePresenter.InitialiseStage(this);

            this._currentPresenter = stagePresenter;
            this._currentStageIndex = stageIndex;
        }

        private void InitialiseStages()
        {
            var presenterTypes = this.StagePresenterTypes;
            presenterTypes.ThrowIfNull("Subclass must provide a list of stage presenter types");

            //TODO: throw new exception type(s)?
            //at least one stage type must be provided by the subclass, and each of these must be ITaskStagePresenter<T>
            var presenterTypeList = presenterTypes.ToList();
            ExceptionHelper.ThrowIf<ArgumentException>(presenterTypeList.Count == 0, "At least one task stage must be provided");
            ExceptionHelper.ThrowIf<ArgumentException>(!presenterTypeList.All(t => typeof(ITaskStagePresenter<T>).IsAssignableFrom(t)), String.Format("All stage presenters must be of type ITaskPresenter<{0}>", typeof(T).Name));

            //set private member list of stage types
            this._stagePresenterTypes = presenterTypeList;
        }

        public T TaskContext { get; private set; }

        public override IPresenter CurrentPresenter
        {
            get
            {
                if (this._currentPresenter == null)
                {
                    //stages have not yet been loaded so initialise and load first stage 
                    this.InitialiseStages();
                    this.MoveToStage(0);
                }

                Debug.Assert(this._currentPresenter != null);
                return this._currentPresenter;
            }
        }

        protected abstract IEnumerable<Type> StagePresenterTypes { get; }
    }
}
